package cn.com.bluemoon.daps.sync.feignclient;

import cn.com.bluemoon.daps.common.enums.DatabaseType;
import cn.com.bluemoon.daps.common.enums.SyncToEnv;
import cn.com.bluemoon.daps.common.exception.DapThrowException;
import cn.com.bluemoon.daps.common.toolkit.BmAssetUtils;
import cn.com.bluemoon.daps.model.dto.ModelTableDto;
import cn.com.bluemoon.daps.sync.core.sync.GlobalConfig;
import cn.com.bluemoon.daps.sync.core.sync.GlobalConfig.TableIdentity;
import cn.com.bluemoon.daps.sync.core.sync.meta.MetadataConverter;
import cn.com.bluemoon.daps.system.entity.DapSystemConfigSandbox;
import cn.com.bluemoon.daps.system.entity.DapSystemDatasource;
import cn.com.bluemoon.daps.system.entity.DapSystemDatasourceEnvironment;
import cn.com.bluemoon.metadata.common.ResultBean;
import cn.com.bluemoon.metadata.common.enums.DbTypeEnum;
import cn.com.bluemoon.metadata.inter.DbMetaDataService;
import cn.com.bluemoon.metadata.inter.dto.in.QueryMetaDataRequest;
import cn.com.bluemoon.metadata.inter.dto.out.ColumnInfoVO;
import cn.com.bluemoon.metadata.inter.dto.out.SchemaInfoVO;
import cn.com.bluemoon.metadata.inter.dto.out.SqlIndexInfoVO;
import cn.com.bluemoon.metadata.inter.dto.out.TableInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 调用元数据服务模块接口的门面
 */
@Service
@Slf4j
public class DapMetadataModuleFacade {
    @Resource
    private DbMetaDataService dbMetaDataService;


    /**
     * 根据提供的数据源获取库表元数据
     *
     * @param dataSourceAndEnv 模型数据源+环境
     * @param tableEngNames 元数据中的表英文名
     * @return 非标准化的元数据
     */
    public SchemaInfoVO getSchemaInfoVO(GlobalConfig.DataSourceAndEnv dataSourceAndEnv, List<String> tableEngNames) {
        DapSystemDatasource ds = dataSourceAndEnv.getDs();
        DapSystemDatasourceEnvironment env = dataSourceAndEnv.getEnv();
        QueryMetaDataRequest req = new QueryMetaDataRequest();
        req.setDbName(env.getDatabaseName());
        req.setIp(env.getHost());
        req.setPort(String.valueOf(env.getPort()));
        try {
            req.setPassword(env.decryptPassword());
        } catch (Exception e) {
            req.setPassword(env.getPassword());
        }
        req.setUsername(env.getUsername());
        String schemaName = DatabaseType.MYSQL.equals(ds.getDatabaseType()) ? env.getDatabaseName() : env.getDatabaseSchema();
        req.setSchemaName(schemaName);
        BmAssetUtils.isTrue(DatabaseType.MYSQL.equals(ds.getDatabaseType()), "目前只支持获取MySQL数据库的元数据信息");
        req.setDbType(DbTypeEnum.MYSQL);
        if (tableEngNames != null && !tableEngNames.isEmpty()) {
            req.setTableNames(tableEngNames.stream().distinct().collect(Collectors.joining(",")));
        }
        ResultBean<SchemaInfoVO> schemaInfoVOResultBean = null;
        try {
            schemaInfoVOResultBean = dbMetaDataService.queryMetaData(req);
        } catch (Exception e) {
            log.error("调用元数据服务:{}, 失败！！", req, e);
            throw new DapThrowException("调用元数据服务失败", e);
        }
        BmAssetUtils.isTrue(schemaInfoVOResultBean.getCode() == 200, schemaInfoVOResultBean.getMsg());
        return schemaInfoVOResultBean.getContent();
    }

    /**
     * 获取沙箱环境的元数据
     *
     * @param globalConfig g
     * @return 获取沙箱环境的元数据
     */
    public SchemaInfoVO getSandboxSchemaInfoVO(GlobalConfig globalConfig) {
        GlobalConfig.DataSourceAndEnv dataSourceAndEnv = globalConfig.getDataSourceAndEnv();
        DapSystemDatasource ds = globalConfig.getDataSource();
        DapSystemDatasourceEnvironment env = dataSourceAndEnv.getEnv();
        QueryMetaDataRequest req = new QueryMetaDataRequest();
        DapSystemConfigSandbox sandbox = globalConfig.getDsSandboxEnv();
        req.setPassword(sandbox.decryptPassword());
        req.setUsername(sandbox.getUsername());
        req.setIp(sandbox.getHost());
        req.setPort(String.valueOf(sandbox.getPort()));
        log.warn("当前配置为同步到沙箱环境中");
        req.setDbName(env.getDatabaseName());
        DatabaseType dbType = ds.getDatabaseType();
        BmAssetUtils.isTrue(DatabaseType.MYSQL.equals(dbType), "支持MySQL数据库");
        req.setDbType(DbTypeEnum.MYSQL);
        req.setSchemaName(env.getDatabaseName());
        ResultBean<SchemaInfoVO> schemaInfoVOResultBean = dbMetaDataService.queryMetaData(req);
        BmAssetUtils.isTrue(schemaInfoVOResultBean.getCode() == 200, schemaInfoVOResultBean
                .getMsg());
        return schemaInfoVOResultBean.getContent();
    }

    /**
     * 根据提供的数据源获取库表元数据
     *
     * @param globalConfig 模型数据源
     * @return 标准化的元数据
     */
    public Tuple2<String, List<ModelTableDto>> getMetadataDataModelTableDtos(GlobalConfig globalConfig) {
        SchemaInfoVO schemaInfoVO = getSchemaInfoVO(globalConfig.getDataSourceAndEnv(), globalConfig.getTableIdentityOpt()
                .map(TableIdentity::getTableNamesDistinct).orElse(null));
        String databaseName = globalConfig.getDataSourceEnv().getDatabaseName();
        SyncToEnv syncToEnv = Optional.ofNullable(globalConfig.getSyncToEnv()).orElse(SyncToEnv.MODEL);
        if (schemaInfoVO.getName() == null) {
            log.warn("{}环境下，当前数据库不存在该数据库名{}或者表信息", syncToEnv.getDesc(), databaseName);
            boolean status = globalConfig.initDbName(false);
            log.warn(String.format("初始化数据库名%s情况：%s", databaseName, status));
            return null;
        }
        if (schemaInfoVO.getTables() == null || schemaInfoVO.getTables().isEmpty()) {
            log.warn("{}环境下，当前数据库不存在该数据库名{}或者表信息", syncToEnv.getDesc(), databaseName);
            return null;
        }
//        BmAssetUtils.notNull(schemaInfoVO.getName(), "元数据获取数据库schema/dbname为空！");
        List<TableInfoVO> tables = schemaInfoVO.getTables();
        List<ModelTableDto> tableDtos = tables.stream().map(t -> {
            List<ColumnInfoVO> columns = t.getColumns();
            List<SqlIndexInfoVO> sqlIndexes = t.getSqlIndexes();
            if (sqlIndexes == null) {
                sqlIndexes = Collections.emptyList();
            }
            if (columns == null) {
                columns = Collections.emptyList();
            }
            return ModelTableDto.builder()
                    .schemaName(schemaInfoVO.getName())
                    .table(MetadataConverter.Table.mgToLocal(t))
                    .fields(columns.stream().map(MetadataConverter.Field::mgToLocal).filter(Objects::nonNull).collect(Collectors.toList()))
                    .indexes(sqlIndexes.stream().map(MetadataConverter.Index::mgToLocal).filter(Objects::nonNull).collect(Collectors.toList()))
                    .partitions(Collections.emptyList())
                    .build();
        }).collect(Collectors.toList());
        return Tuples.of(schemaInfoVO.getName(), tableDtos);
    }
}
